/*
 * Copyright 2012 Marsrutai.lt
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package lt.marsrutai.mobile.android.activity;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.activity.intent.StopMapIntentHolder;
import lt.marsrutai.mobile.android.activity.intent.StopTimesIntentHolder;
import lt.marsrutai.mobile.android.activity.intent.TaskIntentHolder;
import lt.marsrutai.mobile.android.adapter.MultiInfoWindowAdapter;
import lt.marsrutai.mobile.android.fragment.task.GetStopsTaskDialogFragment;
import lt.marsrutai.mobile.android.fragment.task.TaskDialogFragment;
import lt.marsrutai.mobile.android.manager.ActivityManager;
import lt.marsrutai.mobile.android.model.Stop;
import lt.marsrutai.mobile.android.model.VehiclePosition;
import lt.marsrutai.mobile.android.task.GetStopsTask;
import lt.marsrutai.mobile.android.task.GetVehiclePositionsTask;
import lt.marsrutai.mobile.android.task.GetVehiclePositionsTask.Criteria;
import lt.marsrutai.mobile.android.util.StringUtils;
import lt.marsrutai.mobile.android.util.VehiclePositionManager;
import lt.marsrutai.mobile.android.util.VehiclePositionManager.GetVehiclePositionsTaskCriteriaProvider;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager;
import android.view.ViewGroup;

import com.actionbarsherlock.app.ActionBar;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class StopMapActivity extends BaseMapActivity implements OnInfoWindowClickListener {

    private static final String GET_STOPS_TASK_FRAGMENT_TAG = "getStopsTaskFragment";

    private VehiclePositionManager vehiclePositionManager;
    private FragmentManager fragmentManager;
    private ActivityManager manager;

    private String scopeFolder;
    private String scopeName;
    private String trackId;
    private String trackName;
    private String stopId;
    private String scheduleId;
    private String scheduleName;
    private String scheduleTrackName;
    private String transportId;
    private String title;
    private List<Stop> stops;
    
    /**
     * Maps stops to their respective markers
     */
    private Map<String, Stop> markerMap = new HashMap<String, Stop>();

    private BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            TaskIntentHolder holder = new TaskIntentHolder(intent);
            stops = holder.getListResult();

            if (holder.getStatus() == GetStopsTask.RESULT_NOT_RESOLVED) {
                manager.runOnUiThread(manager.errorRes);
            }
            else {
                drawStops(true);
            }
        }
    };

    private BroadcastReceiver vehiclePositionReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            TaskIntentHolder holder = new TaskIntentHolder(intent);
            List<VehiclePosition> vehiclePositions = holder.getListResult();

            if (holder.getStatus() == GetVehiclePositionsTask.RESULT_NOT_RESOLVED) {
                manager.makeToast(StopMapActivity.this, getString(R.string.vehicle_position_could_not_acquire));
            }
            else {
                if (isMapReady() && vehiclePositionManager != null) {
                    vehiclePositionManager.drawVehiclePositions(getMap(), vehiclePositions);
                }
            }
        }
    };


    protected int getNavigationMode() {
        return ActionBar.NAVIGATION_MODE_STANDARD;
    }

    @Override
    public void onCreateInternal(Bundle savedInstanceState) {
        super.onCreateInternal(savedInstanceState);

        fragmentManager = getSupportFragmentManager();
        manager = new ActivityManager(this);

        restoreState(savedInstanceState);

        if (scopeFolder != null && transportId != null && scheduleName != null) {
            vehiclePositionManager = new VehiclePositionManager(this);
            vehiclePositionManager.setTaskCriteriaProvider(new GetVehiclePositionsTaskCriteriaProvider() {
                
                @Override
                public Criteria[] getCriteria() {
                    return new Criteria[] {new Criteria(scopeFolder, transportId, scheduleName)};
                }
            });
            getTopRightControls().add(vehiclePositionManager.setUpControls((ViewGroup) findViewById(R.id.map_container)));
        }
    }
    
    @Override
    protected void setUpMap(GoogleMap map, Bundle savedInstanceState) {
        super.setUpMap(map, savedInstanceState);
        
        MultiInfoWindowAdapter adapter = new MultiInfoWindowAdapter();
        if (vehiclePositionManager != null) {
            adapter.registerAdapter(vehiclePositionManager.getInfoWindowAdapter());
        }
        map.setInfoWindowAdapter(adapter);
        map.setOnInfoWindowClickListener(this);

        // zoom only if map is being set up for the first time
        drawStops(savedInstanceState == null);
    }
    
    @Override
    protected void onResumeInternal() {
        super.onResumeInternal();

        LocalBroadcastManager.getInstance(this).registerReceiver(stopReceiver, new IntentFilter(GetStopsTask.BROADCAST_ACTION));
        LocalBroadcastManager.getInstance(this).registerReceiver(vehiclePositionReceiver, new IntentFilter(GetVehiclePositionsTask.BROADCAST_ACTION));

        TaskDialogFragment<?, ?, ?> taskFragment = (TaskDialogFragment<?, ?, ?>) fragmentManager.findFragmentByTag(GET_STOPS_TASK_FRAGMENT_TAG);

        // Start downloading only if we don't have any scopes, and task fragments has not been retained
        if (!TaskDialogFragment.isActive(taskFragment) && stops == null) {
            TaskDialogFragment.download(fragmentManager, 
                    GetStopsTaskDialogFragment.class, GET_STOPS_TASK_FRAGMENT_TAG, 
                    new GetStopsTask.Criteria[] {new GetStopsTask.Criteria(scopeFolder, trackId, stopId)});
        }

        if (vehiclePositionManager != null) {
            vehiclePositionManager.resume();
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        // Unregister since the activity is about to be closed.
        LocalBroadcastManager.getInstance(this).unregisterReceiver(stopReceiver);
        LocalBroadcastManager.getInstance(this).unregisterReceiver(vehiclePositionReceiver);
        
        // cancel the timer
        if (vehiclePositionManager != null) {
            vehiclePositionManager.cancel();
            vehiclePositionManager.clear();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        StopMapIntentHolder intentHolder = new StopMapIntentHolder(new Bundle());
        intentHolder.setScopeFolder(scopeFolder);
        intentHolder.setScopeName(scopeName);
        intentHolder.setStopId(stopId);
        intentHolder.setTrackId(trackId);
        intentHolder.setTrackName(trackName);
        intentHolder.setScheduleId(scheduleId);
        intentHolder.setScheduleName(scheduleName);
        intentHolder.setScheduleTrackName(scheduleTrackName);
        intentHolder.setTransportId(transportId);
        intentHolder.setStops(stops);
        intentHolder.setTitle(title);

        outState.putAll(intentHolder.getIntent().getExtras());

        super.onSaveInstanceState(outState);
    }
    
    private void restoreState(Bundle bundle) {
        StopMapIntentHolder intentHolder = new StopMapIntentHolder(bundle == null ? getIntent().getExtras() : bundle);
        scopeFolder = intentHolder.getScopeFolder();
        scopeName = intentHolder.getScopeName();
        stopId = intentHolder.getStopId();
        trackId = intentHolder.getTrackId();
        trackName = intentHolder.getTrackName();
        scheduleId = intentHolder.getScheduleId();
        scheduleName = intentHolder.getScheduleName();
        scheduleTrackName = intentHolder.getScheduleTrackName();
        transportId = intentHolder.getTransportId();
        stops = intentHolder.getStops();
        title = intentHolder.getTitle();
    }

    @Override
    public void onInfoWindowClick(Marker marker) {
        Stop stop = markerMap.get(marker.getId());
        
        if (stop == null) {
            return;
        }
        
        StopTimesIntentHolder intentHolder = new StopTimesIntentHolder(this);
        intentHolder.setScopeFolder(scopeFolder);
        intentHolder.setScopeName(scopeName);
        intentHolder.setTransportId(transportId);
        intentHolder.setScheduleId(scheduleId);
        intentHolder.setScheduleName(scheduleName);
        intentHolder.setScheduleTrackName(scheduleTrackName);
        intentHolder.setTrackId(trackId);
        intentHolder.setTrackName(trackName);
        intentHolder.setStopId(stop.getId());
        intentHolder.setStopName(stop.getName());
        intentHolder.setStopDirectionName(stop.getDirectionName());

        startActivity(intentHolder.getIntent());
    }

    /**
     * Draws the stops/route on the map
     * 
     * @param zoomToItems
     *            Indicates whether map should zoom to the drawn items
     */
    private void drawStops(boolean zoomToItems) {
        if (!isMapReady()) {
            return;
        }

        if (stops != null && stops.size() > 0) {
            // sometimes we need to zoom into one particular point
            LatLng pointToZoomInto = null;
            final List<LatLng> geoPoints = new LinkedList<LatLng>();

            for (Stop stop : stops) {
                if (stop.getLatitude() == null || stop.getLongitude() == null) {
                    continue;
                }

                LatLng point = new LatLng(stop.getLatitude().doubleValue(), stop.getLongitude().doubleValue());
                geoPoints.add(point);

                MarkerOptions markerOptions = new MarkerOptions().position(point).title(stop.getName()).snippet(stop.getDirectionName());
                BitmapDescriptor icon = null;

                if (StringUtils.equals(stop.getId(), stopId)) {
                    icon = BitmapDescriptorFactory.fromResource(R.drawable.segment_start);
                    pointToZoomInto = point;
                }
                else if (stops.indexOf(stop) == 0) {
                    icon = BitmapDescriptorFactory.fromResource(R.drawable.first_stop);
                }
                else if (stops.indexOf(stop) == stops.size() - 1) {
                    icon = BitmapDescriptorFactory.fromResource(R.drawable.last_stop);
                }
                else {
                    icon = BitmapDescriptorFactory.fromResource(R.drawable.all_stops);
                }
                markerOptions.icon(icon);
                Marker marker = getMap().addMarker(markerOptions);
                markerMap.put(marker.getId(), stop);
            }

            if (geoPoints.size() == 0) {
                manager.makeToast(StopMapActivity.this, getString(stops.size() > 1 ? R.string.track_location_unknown
                        : R.string.stop_location_unknown));
            }
            else if (zoomToItems) {
                if (pointToZoomInto != null) {
                    zoomToItems(pointToZoomInto);
                }
                else {
                    zoomToItems(geoPoints);
                }
            }
        }
    }
}
